package com.ryan.ohos.extension.nested.component;

import com.ryan.ohos.extension.event.interfaces.ViewGroup;
import com.ryan.ohos.extension.nested.*;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.ScrollView;
import ohos.app.Context;
import ohos.multimodalinput.event.TouchEvent;

/**
 * 实现了{@link NestedScrollingParent}与{@link NestedScrollingChild}接口的ListContainer
 */
public class NestedScrollView extends ScrollView
        implements Component.TouchEventListener, ViewGroup,
        NestedScrollingChild, NestedScrollingParent, ScrollingView {

    private final ScrollHelper helper;
    private final NestedScrollingParentHelper mParentHelper;

    private int direction;

    public NestedScrollView(Context context) {
        this(context, null);
    }

    public NestedScrollView(Context context, AttrSet attrSet) {
        this(context, attrSet, null);
    }

    public NestedScrollView(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);

        helper = new ScrollHelper(this);
        mParentHelper = new NestedScrollingParentHelper(getContainer());

        setTouchEventListener(this);
        setCanAcceptScrollListener((component, i, b) -> false);

        // todo setReboundEffect(true);
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        return helper.onTouchEvent(component, touchEvent);
    }

    // ScrollingView

    @Override
    public ComponentContainer getContainer() {
        return this;
    }

    @Override
    public int getScrollY() {
        return getScrollValue(AXIS_Y);
    }

    @Override
    public int getScrollX() {
        return getScrollValue(AXIS_X);
    }

    @Override
    public int getDirection() {
        return direction;
    }

    @Override
    public void scrollByInternal(int deltaX, int deltaY) {
        scrollBy(deltaX, deltaY);
    }

    @Override
    public boolean disallowInterceptWhenMoving() {
        return true;
    }

    // ViewGroup

    @Override
    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
        helper.requestDisallowInterceptTouchEvent(disallowIntercept);
    }

    @Override
    public boolean onInterceptTouchEvent(TouchEvent ev) {
        return helper.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean dispatchTouchEvent(TouchEvent ev) {
        return helper.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(TouchEvent ev) {
        if (ev.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
            checkDirection();
        }
        return helper.onTouchEvent(ev);
    }

    @Override
    public boolean isConsumed() {
        return helper.isConsumed();
    }

    // NestedScrollingChild

    @Override
    public void setNestedScrollingEnabled(boolean enabled) {
        helper.setNestedScrollingEnabled(enabled);
    }

    @Override
    public boolean isNestedScrollingEnabled() {
        return helper.isNestedScrollingEnabled();
    }

    @Override
    public boolean startNestedScroll(int axes, int type) {
        return helper.startNestedScroll(axes, type);
    }

    @Override
    public void stopNestedScroll(int type) {
        helper.stopNestedScroll(type);
    }

    @Override
    public boolean hasNestedScrollingParent(int type) {
        return helper.hasNestedScrollingParent(type);
    }

    @Override
    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow, int type, int[] consumed) {
        return helper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
                offsetInWindow, type, consumed);
    }

    @Override
    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow, int type) {
        return helper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, type);
    }

    @Override
    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
        return helper.dispatchNestedFling(velocityX, velocityY, consumed);
    }

    @Override
    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
        return helper.dispatchNestedPreFling(velocityX, velocityY);
    }

    // NestedScrollingParent

    @Override
    public boolean onStartNestedScroll(Component child, Component target, int axes, int type) {
        return true;
    }

    @Override
    public void onNestedScrollAccepted(Component child, Component target, int axes, int type) {
        mParentHelper.onNestedScrollAccepted(child, target, axes, type);
        startNestedScroll(axes, type);
    }

    @Override
    public void onStopNestedScroll(Component target, int type) {
        mParentHelper.onStopNestedScroll(target, type);
        stopNestedScroll(type);
    }

    @Override
    public void onNestedScroll(Component target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type, int[] consumed) {
        onNestedScrollInternal(dxConsumed, dyUnconsumed, type, consumed);
    }

    @Override
    public void onNestedPreScroll(Component target, int dx, int dy, int[] consumed, int type) {
        dispatchNestedPreScroll(dx, dy, consumed, null, type);
    }

    @Override
    public boolean onNestedFling(Component target, float velocityX, float velocityY, boolean consumed) {
        if (!consumed) {
            dispatchNestedFling(velocityX, velocityY, true);
            helper.fling((int) velocityX, (int) velocityY);
            return true;
        }
        return false;
    }

    @Override
    public boolean onNestedPreFling(Component target, float velocityX, float velocityY) {
        return dispatchNestedPreFling(velocityX, velocityY);
    }

    @Override
    public int getNestedScrollAxes() {
        return mParentHelper.getNestedScrollAxes();
    }

    private void onNestedScrollInternal(int dxUnconsumed, int dyUnconsumed, int type, int[] consumed) {
        final int oldScrollX = getScrollValue(AXIS_X);
        final int oldScrollY = getScrollValue(AXIS_Y);

        scrollBy(dxUnconsumed, dyUnconsumed);

        final int myConsumedX = getScrollValue(AXIS_X) - oldScrollX;
        final int myConsumedY = getScrollValue(AXIS_Y) - oldScrollY;

        if (consumed != null) {
            consumed[0] += myConsumedX;
            consumed[1] += myConsumedY;
        }

        final int myUnconsumedX = dxUnconsumed - myConsumedX;
        final int myUnconsumedY = dyUnconsumed - myConsumedY;

        helper.dispatchNestedScroll(myConsumedX, myConsumedY, myUnconsumedX, myUnconsumedY, null, type, consumed);
    }

    private void checkDirection() {
        if (getChildCount() <= 0) {
            return;
        }

        Component child = getComponentAt(0);
        direction = child.getWidth() > child.getHeight() ?
                NestedScrollingHelper.SCROLL_AXIS_HORIZONTAL : NestedScrollingHelper.SCROLL_AXIS_VERTICAL;
    }
}
